package heap

import (
	"fmt"
	"hi-jvm/classfile"
	"hi-jvm/classpath"
)

/*
类加载器
依赖ClassPath搜索和读取class文件
 */
/*
class names:
    - primitive types: boolean, byte, int ...
    - primitive arrays: [Z, [B, [I ...
    - non-array classes: java/lang/Object ...
    - array classes: [Ljava/lang/Object; ...
*/
type ClassLoader struct {
	Cp       *classpath.Classpath	// ClassPath指针
	ClassMap map[string]*Class // loaded classes  存储已经加载的类数据
}

func NewClassLoader(cp *classpath.Classpath) *ClassLoader {
	return &ClassLoader{
		Cp:       cp,
		ClassMap: make(map[string]*Class),
	}
}

/*
把类数据加载到方法区
 */
func (self *ClassLoader) LoadClass(name string) *Class {
	class, ok := self.ClassMap[name];
	if  ok {
		// already loaded
		return class
	}

	if name[0] == '[' {
		return self.loadArrayClass(name)
	}

	return self.loadNonArrayClass(name)
}

/*
加载非数组类， 数据类再运行期间生成。
 */
func (self *ClassLoader) loadNonArrayClass(name string) *Class {
	data, entry := self.readClass(name)	// 找到class
	class := self.defineClass(data)		// 解析文件， 放入方法区
	link(class)		// 链接
	fmt.Printf("[Loaded %s from %s]\n", name, entry)
	return class
}


func (self *ClassLoader) loadArrayClass(name string) *Class {
	class := &Class{
		AccessFlags: ACC_PUBLIC, // todo
		Name:        name,
		Loader:      self,
		InitStarted: true,
		SuperClass:  self.LoadClass("java/lang/Object"),
		Interfaces: []*Class{
			self.LoadClass("java/lang/Cloneable"),
			self.LoadClass("java/io/Serializable"),
		},
	}
	self.ClassMap[name] = class
	return class
}

func (self *ClassLoader) readClass(name string) ([]byte, classpath.Entry) {
	data, entry, err := self.Cp.ReadClass(name)
	if err != nil {
		panic("java.lang.ClassNotFoundException: " + name)
	}
	return data, entry
}

// jvms 5.3.5
func (self *ClassLoader) defineClass(data []byte) *Class {
	class := parseClass(data)	// 转换为class结构体
	class.Loader = self
	resolveSuperClass(class)
	resolveInterfaces(class)
	self.ClassMap[class.Name] = class
	return class
}

func parseClass(data []byte) *Class {
	cf, err := classfile.Parse(data)
	if err != nil {
		//panic("java.lang.ClassFormatError")
		panic(err)
	}
	return newClass(cf)
}

/*
解析超类符号
除Object外，都有超类，递归加载
 */
// jvms 5.4.3.1
func resolveSuperClass(class *Class) {
	if class.Name != "java/lang/Object" {
		class.SuperClass = class.Loader.LoadClass(class.SuperClassName)
	}
}

/*
解析接口符号
接口可以有多个， 每个接口递归加载
 */
func resolveInterfaces(class *Class) {
	interfaceCount := len(class.InterfaceNames)
	if interfaceCount > 0 {
		class.Interfaces = make([]*Class, interfaceCount)
		for i, interfaceName := range class.InterfaceNames {
			class.Interfaces[i] = class.Loader.LoadClass(interfaceName)
		}
	}
}

/*
连接，分为验证和准备两个阶段
 */
func link(class *Class) {
	verify(class)
	prepare(class)
}

func verify(class *Class) {
	// todo
}

/*
计算实例字段和静态字段的个数（和长度）， 然后分配空间
 */
// jvms 5.4.2
func prepare(class *Class) {
	calcInstanceFieldSlotIds(class)
	calcStaticFieldSlotIds(class)
	allocAndInitStaticVars(class)
}

/*
计算实例字段个数
1： 查找超类实例字段个数（长度）
2： 遍历本类中的所有实例字段， 并增加个数（长度）
依赖：
1： 计算从根类（object）开始，层层计算
 */
func calcInstanceFieldSlotIds(class *Class) {
	slotId := uint(0)
	if class.SuperClass != nil {
		slotId = class.SuperClass.InstanceSlotCount
	}
	for _, field := range class.Fields {
		if field.IsStatic() {
			continue
		}
		field.SlotId = slotId
		slotId++
		if field.isLongOrDouble() {
			slotId++
		}
	}
	class.InstanceSlotCount = slotId
}

/*
计算静态字段量个数（长度）
 */
func calcStaticFieldSlotIds(class *Class) {
	slotId := uint(0)
	for _, field := range class.Fields {
		if !field.IsStatic() {
			continue
		}
		field.SlotId = slotId
		slotId++
		if field.isLongOrDouble() {
			slotId++
		}
	}
	class.StaticSlotCount = slotId
}

/*
给类静态变量分配空间并赋初始值
 */
func allocAndInitStaticVars(class *Class) {
	class.StaticVars = newSlots(class.StaticSlotCount)
	for _, field := range class.Fields {
		if field.isConst() {
			initStaticFinalVar(class, field)
		}
	}
}

/*
为常量赋值
 */
func initStaticFinalVar(class *Class, field *Field) {
	vars := class.StaticVars
	cp := class.ConstantPool
	cpIndex := field.ConstValueIndex
	slotId := field.SlotId

	if cpIndex < 0 {
		return
	}

	if field.IsInt32() {
		val := cp.GetConstant(cpIndex).(int32)
		vars.SetInt(slotId, val)
		return
	}

	if field.IsInt64() {
		val := cp.GetConstant(cpIndex).(int64)
		vars.SetLong(slotId, val)
		return
	}
	if field.IsFloat() {
		val := cp.GetConstant(cpIndex).(float32)
		vars.SetFloat(slotId, val)
		return
	}
	if field.IsDouble() {
		val := cp.GetConstant(cpIndex).(float64)
		vars.SetDouble(slotId, val)
		return
	}
	if field.isString() {
		panic("todo")
	}
}
